package com.bosintek.wordtest.gui;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.nio.charset.Charset;
import java.util.Vector;

import android.app.Activity;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.bosintek.wordtest.Bank;
import com.bosintek.wordtest.R;
import com.bosintek.wordtest.WordSet;
import com.bosintek.wordtest.util.DataManager;
import com.bosintek.wordtest.util.ObjectsManager;
import com.bosintek.wordtest.util.Parser;
import com.bosintek.wordtest.webAccess.Loader;

/**
 * 
 * @author F. Javier R. Donado
 * 
 */
public class WordTest extends Activity implements OnClickListener {
	private Vector<WordSet> wsets;
	private Vector<WordSet> allWsets;
	private Vector<WordSet> marked;
	private Bank mBank; // Bank of Marked WordSets
	private WordSet actWset;
	private int index = 0;
	private boolean lastSet = false;
	private boolean wordShown = false;
	private boolean meaningShown = false;
	private boolean forward = true;
	private TextView txtWord;
	private TextView txtMeaning;
	private TextView txtRemaining;
	private TextView txtBankName;
	private Button btnDir;
	private Button btnNxtSet;
	private Button btnMark;
	private LinearLayout wSetsLayout;

	/**
	 * Called when the activity is first created.
	 */
	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.wtest);
		mySetup();
	}

	@Override
	public void onResume() {
		super.onResume();
		// If a new bank must be loaded, now it's the time.
		if (ObjectsManager.isMustReload()) {
			loadData(false);
		}
		ObjectsManager.setMustReload(false);
	}

	/**
	 * It processes the user events.
	 */
	public void onClick(View v) {
		// Intent myIntent;
		switch (v.getId()) {
		case R.id.btnDir:
			changeDir();
			break;
		case R.id.btnSet:
			nextSet();
			break;
		case R.id.btnMark:
			if (actWset.isMarked()) {
				unmark();
			} else {
				mark();
			}
			break;
		case R.id.WSetsLayout:
			if (forward) {
				goForward();
			} else {
				goBackwards();
			}
			updateMarked();
			break;
		}
	}

	/**
	 * Removes a WordSet from the marked WordSets list.
	 */
	private void unmark() {
		actWset.setMarked(false);
		for (int i = 0; i < marked.size(); i++) {
			if (marked.get(i).equals(actWset)) {
				marked.remove(i--);
			}
		}
		if (!lastSet) {
			allWsets.remove(actWset);
		}
		// marked.txt is updated.
		saveWSet(marked, "marked.txt");
		// Marked Bank is updated.
		mBank.setwSets(marked);
		updateMarked();
	}

	/**
	 * Adds a new WordSet to the marked WordSets list.
	 */
	private void mark() {
		if (/* !isAlready(actWset) */!actWset.isMarked()) {
			actWset.setMarked(true);
			this.marked.add(actWset);
			if (!lastSet) {
				allWsets.add(actWset);
			}
			// marked.txt is updated.
			saveWSet(marked, "marked.txt");
			// Marked Bank is updated.
			mBank.setwSets(marked);
			updateMarked();
		}
	}

	/**
	 * It saves a WordSet objects Vector into a file.
	 * 
	 * @param wSets
	 * @param fName
	 */
	public void saveWSet(Vector<WordSet> wSets, String fName) {

		try {
			BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(
					new FileOutputStream(new File(
							"/sdcard/com.bosintek.wordtest/" + fName)),
					Charset.forName("ISO-8859-1")));
			bw.flush();
			int i = 0;
			while (i < wSets.size()) {
				bw.write(wSets.get(i).getWord() + " - "
						+ wSets.get(i).getMeaning());
				bw.newLine();
				i++;
			}
			bw.close();

		} catch (Exception e) {
			Toast.makeText(getApplicationContext(), e.getMessage(), 2500);
			// System.out.println("Error de escritura: " + e.getMessage());
		}
	}

	/**
	 * It looks for an equal WordSet within all the marked WordSets. In case the
	 * WordSet is already in the list, it returns true.
	 * 
	 * @param wSet
	 * @return
	 */
	private boolean isAlready(WordSet wSet) {
		int i = 0;
		while (i < marked.size()) {
			if (wSet.getWord().equals(marked.get(i).getWord())
					&& wSet.getMeaning().equals(marked.get(i).getMeaning())) {
				return true;
			}
			i++;
		}
		return false;
	}

	/**
	 * Initializes all the objects needed for the app.
	 */
	public void mySetup() {
		// Elements used
		txtWord = (TextView) findViewById(R.id.textView1);
		txtMeaning = (TextView) findViewById(R.id.textView2);
		txtBankName = (TextView) findViewById(R.id.textBankName);
		txtRemaining = (TextView) findViewById(R.id.textRemaining);
		btnDir = (Button) findViewById(R.id.btnDir);
		btnDir.setBackgroundColor(Color.rgb(240, 240, 255));
		btnNxtSet = (Button) findViewById(R.id.btnSet);
		btnMark = (Button) findViewById(R.id.btnMark);
		btnMark.setBackgroundColor(Color.rgb(240, 255, 240));
		wSetsLayout = (LinearLayout) findViewById(R.id.WSetsLayout);

		// Click listeners
		btnNxtSet.setOnClickListener(this);
		btnDir.setOnClickListener(this);
		btnMark.setOnClickListener(this);
		wSetsLayout.setOnClickListener(this);

		// Basic structures PROVISIONAL
		this.wsets = new Vector<WordSet>(); // This is the set that contains the
											// current up-to-seven word/meaning
											// pairs.

		// No new bank will be loaded until the user requests it.
		ObjectsManager.setMustReload(false);

		// this.allWsets = new Vector<WordSet>();

		wsets.add(new WordSet("", ""));
		this.actWset = wsets.elementAt(0);
		loadData(true);
	}

	/**
	 * Loads the data to be shown. At the beginning, the last state is loaded.
	 * 
	 * @param init
	 *            True only at the time of initialization in order to restore
	 *            the last state.
	 */
	private void loadData(boolean init) {
		// Data
		this.lastSet = false;
		Loader loader = new Loader();
		boolean isMarked = false;
		if (init) {
			DataManager dm = new DataManager(this);
			ObjectsManager.setPass(dm.getPass());
			ObjectsManager.setUser(dm.getUser());
			// An xml standing for the local banks is loaded at the begining.
			String xml = dm.getBanksXML();
			Parser p = new Parser();
			ObjectsManager.setBanks(p.parseXML(xml));
		}
		ObjectsManager.setActivity(this);
		this.allWsets = new Vector<WordSet>();

		// The current bank is loaded
		if (ObjectsManager.getBanks().size() > 0 && !init) {
			Bank bank = ObjectsManager.getCurrentBank();
			if(bank.getID()=="0"){
				isMarked=true;
			}
			this.txtBankName.setText("  " + bank.getName());
			this.allWsets.addAll(bank.getwSets());
			// At the beginning, the last state is restored
		} else if (init) {
			this.allWsets.addAll(loader.loadPlainBanks("lastState.txt"));
			this.txtBankName.setText("  Last state");
			// When the user tries to open a bank and there's no bank available,
			// only marked sets will be opened.
		} else {
			this.txtBankName.setText("  No bank available (only marked sets)");
		}
		// Marked WordSets are loaded from file.
		this.marked = loader.loadPlainBanks("marked.txt");
		// Marked WordSets are also loaded to the allWsets vector and its Bank
		// is updated.
		if (marked != null) {
			if (!init && !isMarked) {
				this.allWsets.addAll(marked);
			}
			mBank = new Bank();
			mBank.setwSets(marked);
			mBank.setID("0");
			mBank.setName("Marked");
			ObjectsManager.setMarked(mBank);
		} else {
			// In case there are no marked WordSets, the vector is initialized.
			this.marked = new Vector<WordSet>();
		}
		if (this.allWsets.size() > 0) {
			this.btnNxtSet.setEnabled(true);
			findMarked();
		}
		this.txtRemaining.setText(" " + this.allWsets.size() + "  ");
	}

	/**
	 * It finds which sets are marked among all the WordSets.
	 * 
	 * It is necessary to make a comparison between each object because there
	 * might be different instances of two equal WordSets.
	 */
	private void findMarked() {
		for (int i = 0; i < this.allWsets.size(); i++) {
			for (int j = 0; j < this.marked.size(); j++) {
				if (marked.get(j).equals(allWsets.get(i))) {
					allWsets.get(i).setMarked(true);
					break;
				}
			}
		}
	}

	/**
	 * Changes the direction a WordSet is asked (word or meaning asked first).
	 */
	public void changeDir() {
		if (forward) {
			forward = false;
			btnDir.setText(R.string.dirBackwards);
			btnDir.setBackgroundColor(Color.rgb(240, 240, 240));

		} else {
			forward = true;
			btnDir.setText(R.string.dirForward);
			btnDir.setBackgroundColor(Color.rgb(240, 240, 255));
		}
		index = (1 + index) % (wsets.size());
		actWset = wsets.elementAt(index);
		txtWord.setText("");
		txtMeaning.setText("");
		wordShown = false;
		meaningShown = false;
	}

	/**
	 * Returns the next word.
	 */
	private void goForward() {
		if (!wordShown) {
			txtWord.setText(actWset.getWord());
			wordShown = true;
		}
		// When there's a word but no meaning, the meaning is shown.
		else if (!meaningShown) {
			txtMeaning.setText(this.actWset.getMeaning());
			this.meaningShown = true;
		} else {
			// Otherwise, a new word must be shown
			index = (1 + index) % (wsets.size());
			actWset = wsets.elementAt(index);
			txtWord.setText(actWset.getWord());
			txtMeaning.setText("");
			wordShown = true;
			meaningShown = false;
		}
	}

	/**
	 * Returns the next meaning.
	 */
	private void goBackwards() {
		if (!meaningShown) {
			txtMeaning.setText(actWset.getMeaning());
			meaningShown = true;
		}
		// When there's a word but no meaning, the meaning is shown.
		else if (!wordShown) {
			txtWord.setText(this.actWset.getWord());
			this.wordShown = true;
		} else {
			// Otherwise, a new word must be shown
			index = (1 + index) % (wsets.size());
			actWset = wsets.elementAt(index);
			txtMeaning.setText(actWset.getMeaning());
			txtWord.setText("");
			wordShown = false;
			meaningShown = true;
		}
	}

	/**
	 * Generates a new set of 7 words.
	 */
	public void nextSet() {

		index = 0;
		txtMeaning.setText("");
		txtWord.setText("");
		wordShown = false;
		meaningShown = false;

		int tmp;

		wsets = new Vector<WordSet>();
		if (lastSet) {
			wsets.add(new WordSet("NO WORDS LEFT", "NO WORDS LEFT"));
			actWset = wsets.get(0);
			return;
		}
		if (allWsets.size() > 7) {
			for (int i = 1; i <= 7; i++) {
				tmp = (int) Math.floor(Math.random() * allWsets.size());
				wsets.add(allWsets.get(tmp));
				allWsets.remove(tmp);
			}
		} else {
			lastSet = true;
			this.btnNxtSet.setEnabled(false);
			while (allWsets.size() > 0) {
				tmp = (int) Math.floor(Math.random() * allWsets.size());
				wsets.add(allWsets.get(tmp));
				allWsets.remove(tmp);
			}
		}
		if (wsets.size() == 0) {
			wsets.add(new WordSet("", ""));
		}
		actWset = wsets.get(0);
		updateMarked();
		this.txtRemaining.setText(" " + this.allWsets.size() + "  ");
		saveState();
	}

	/**
	 * It updates the appearance of the mark/unmark button.
	 */
	private void updateMarked() {
		if (actWset.isMarked()) {
			btnMark.setText("Unmark");
			btnMark.setBackgroundColor(Color.rgb(255, 240, 240));
		} else {
			btnMark.setText("Mark");
			btnMark.setBackgroundColor(Color.rgb(240, 255, 240));
		}
	}

	/**
	 * It saves the current state in order to be able to load it at the
	 * beginning.
	 */
	private void saveState() {
		Vector<WordSet> temp = new Vector<WordSet>();
		temp.addAll(allWsets);
		temp.addAll(this.wsets);
		// marked.txt is updated.
		saveWSet(temp, "lastState.txt");
	}

	/*
	 * It creates the options menu
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.choices, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle item selection
		switch (item.getItemId()) {
		case R.id.item1:
			findFile();
			return true;
		case R.id.item2:
			showHelp();
			return true;
		case R.id.item3:
			// final EditText mytext = (EditText)findViewById(R.id.editText1);
			// getWords(mytext.getText().toString());
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}

	/**
	 * It loads plain txt banks from a specified location on the SD card.
	 */
	public void findFile() {
		Loader loader = new Loader();
		// TODO: Read the filenames
		// TODO: Do this for each file
		this.allWsets = loader.loadPlainBanks("load/000.txt");
		this.lastSet = false;
		// Move the files to another location
		// TODO: Load each file as a different bank.
		findMarked();
	}

	/*
	 * It shows a pop-up with some help.
	 */
	private void showHelp() {
		// TODO: A simple pop-up will be displayed.
	}

}